package ec_si_template;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import cec2010suite.Function;

/**
 * This class implements the individuals (i.e. particles, chromosomes, fish,
 * etc.)
 * 
 * @author Andreas Janecek
 * 
 */
public class Individual {

	/**
	 * the current location of the individual
	 */
	private double[] p;

	/**
	 * the current fitness of the individual
	 */
	private double fitness;

	/**
	 * the object of the function to be optimized
	 */
	private Function func;

	/**
	 * the index of the individual
	 */
	private int index;

	/**
	 * Neighbour set, from where the parents for the next generation will be
	 * chosen
	 */
	private Individual[] neighbours;

	/**
	 * 3 random selected vectors
	 */

	private Individual v1;
	private Individual v2;
	private Individual v3;

	/**
	 * Constructor: Initialize the individual. Find random position within
	 * initialization range and compute fitness of this position
	 * 
	 * @param func
	 *            : the function object
	 * @param dim
	 *            : the problem dimension
	 * @param minInit
	 *            : lower limit of the initialization range
	 * @param maxInit
	 *            : upper limit of the initialization range
	 * @param minBound
	 *            : lower limit of the search space
	 * @param maxBound
	 *            : upper limit of the search space
	 */
	public Individual(int index, Function func, int dim, double minInit,
			double maxInit, double minBound, double maxBound) {
		this.index = index;
		this.func = func;
		this.p = new double[dim];

		// Initialize the swarm / the population at a new position
		for (int i = 0; i < dim; i++) {
			p[i] = (minInit + (new Random().nextDouble() * (maxInit - minInit)));
		}

		// calculate fitness
		this.fitness = this.func.compute(this.p);
	}

	/**
	 * return the current position
	 * 
	 * @return the current position
	 */
	public double[] getp() {
		return this.p;
	}

	/**
	 * return the current fitness
	 * 
	 * @return the current fitness
	 */
	public double getfitness() {
		return this.fitness;
	}

	/**
	 * return index
	 * 
	 * @return index
	 */
	public int getindex() {
		return this.index;
	}

	/**
	 * update the individual (this is algorithm specific) EXAMPLE: here, a
	 * simple random hill climber is used. If new fitness is better than the old
	 * one, p and fitness are updated
	 * 
	 * @param dim
	 *            : the problem dimension
	 * @param minBound
	 *            : lower limit of the search space
	 * @param maxBound
	 *            : upper limit of the search space
	 */
	public void update(int dim, double minBound, double maxBound) {

		/**
		 * Donor Vector, Mutation Step
		 */
		double[] donor = new double[dim];

		/** Trial Vector, Cross Over step */
		double[] trial = new double[dim];

		chooseRandomVectors();

		/* Creating the DONOR vector */
		for (int i = 0; i < donor.length; i++) {
			donor[i] = v1.p[i] + Config.F * (v2.p[i] - v3.p[i]);
		}
		Random r = new Random();

		/**
		 * Random selected number to ensure at least one attribute from the
		 * DONOR will be used in the Trial Vector
		 */
		int randAttribute = r.nextInt(dim);
		/* Creating the trial vector */
		for (int i = 0; i < donor.length; i++) {
			double temp = r.nextDouble();
			if (temp <= Config.HIGH_CR || i == randAttribute) {
				trial[i] = donor[i];
			} else {
				trial[i] = p[i];
			}
		}

		// calculate fitness for TRIAL
		double trialFitnes = this.func.compute(trial);

		// if new fitness is better than the old one update individual
		if (trialFitnes < this.fitness) {
			this.p = trial;
			this.fitness = trialFitnes;
		}
	}

	private void chooseRandomVectors() {
		Random r = new Random();

		List<Integer> list = new ArrayList<Integer>();
		while (list.size() < 3) {
			int temp = r.nextInt(neighbours.length);
			if (!(list.contains(temp)) && temp != index) {
				list.add(temp);
			}
		}

		v1 = neighbours[list.get(0)];
		v2 = neighbours[list.get(1)];
		v3 = neighbours[list.get(2)];

	}

	public void setNeighbours(Individual[] neighbours) {
		this.neighbours = neighbours;
	}

}
